use hypers::prelude::*;
use std::sync::LazyLock;
use serde::{Deserialize, Serialize};
use tera::{Context, Tera};

const WEB: &str = "web";
static VIEW: LazyLock<Tera> = LazyLock::new(|| Tera::new("template_tera/templates/**/*").unwrap());

pub fn render(ctx: &Context, name: &str) -> Response {
    let mut res = Response::default();
    let body = match VIEW.render(name, ctx) {
        Ok(v) => v,
        Err(e) => {
            res.status(500).body(e.to_string());
            return res;
        }
    };
    res.render(200).render(Text::Html(body))
}

#[hook]
async fn global(mut req: Request, next: Next<'_>) -> impl Responder {
    req.depot.insert(WEB, Context::new());
    next.next(req).await
}

#[derive(Debug, Deserialize, Serialize, Extract)]
pub struct LoginUser {
    account: String,
    password: String,
    remember: bool,
}

#[derive(Debug, Deserialize, Serialize, Extract)]
pub struct RegisteredUser {
    pub account: String,
    pub password: String,
    pub nickname: String,
    pub email: String,
}

struct Web;
#[controller(name = "/",hook = [global])]
impl Web {
    // http://127.0.0.1:7878/home
    #[get("/home")]
    async fn home(req: &mut Request) -> impl Responder {
        let ctx = req.depot.remove::<Context>(WEB).unwrap();
        render(&ctx, "visitor/login.html")
    }
}

struct Api;
#[controller(prefix = "/api/v1", name = "user",hook = [global])]
impl Api {
    #[post("/login")]
    async fn login(_req: &mut Request, user: LoginUser) -> impl Responder {
        println!("登陆参数 = {:?}", user);
        ()
    }

    #[post("/register")]
    async fn register(_req: &mut Request, user: RegisteredUser) -> impl Responder {
        println!("注册参数 = {:?}", user);
        ()
    }
}

#[tokio::main]
async fn main() -> Result<()> {
    let mut root = Router::default();
    root.get("/*", StaticDir::new("template_tera/static"));
    root.push(Web);
    root.push(Api);
    println!("root = {:#?}", root);
    let listener = hypers::TcpListener::bind("127.0.0.1:7878").await?;
    hypers::listen(root, listener).await
}

